using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Newtonsoft.Json.Linq;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.Metadata.Entity.DataModel;

namespace T.FAS.Runtime.DataAccess.Engine
{
    public class TRecordService
    {
        private static readonly TRecordPKComparer s_recordPKComparer = new TRecordPKComparer();
        private static readonly IQueryService<CompositeDataObject> s_saveService = new CDODataQueryService();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        public static TRecord RefreshTRecord(CompositeDataObject cdo, TRecord record)
        {
            var pkVal = record.GetCDOTRecordPrimaryKeyValue(cdo);
            var oldRecord = GetTRecordFromDB(cdo, pkVal);
            GetUpdateChangeSet(cdo, cdo.RootCompositeNodeObject, new List<TRecord> { oldRecord }, new List<TRecord> { record });
            return oldRecord;
        }

        private static void GetUpdateChangeSet(CompositeDataObject cdo, CompositeNodeObject cno, List<TRecord> oldRecords, List<TRecord> newRecords, TRecord parentNewRecord = null, int depth = 0)
        {
            var pkEle = cno.GetPKElement();
            var pkColName = cno.GetPKColumn().ColumnName;

            //顶级节点校验Record是否为同一条TRecord
            if (cno.IsRootObject)
            {
                if (newRecords.Count * oldRecords.Count == 1)
                {
                    var newPk = oldRecords.FirstOrDefault().GetPKValue(cno);
                    var oldPk = newRecords.FirstOrDefault().GetPKValue(cno);
                    if (newPk == null || oldPk == null)
                        throw new FasException(DataAccessErrorCode.PlugIn.ChangeLog.Error, string.Format(CommonStringClassEngine.TRecordService_GetUpdateChangeSet));
                    else if (newPk != oldPk)
                    {
                        throw new FasException(DataAccessErrorCode.PlugIn.ChangeLog.Error, string.Format(CommonStringClassEngine.TRecordService_GetUpdateChangeSet1));
                    }
                }
            }

            List<TRecord> dropRecords = null;                   //需要drop的记录
            List<TRecord> addRecords = null;                    //需要add的记录
            List<object> needCompareRecordIdentifiers = null;   //需要比较的记录的主键

            InitRecord(newRecords, cno);
            InitRecord(oldRecords, cno);

            dropRecords = oldRecords?.Except(newRecords, s_recordPKComparer).ToList();            //一定要指定Comparer
            addRecords = newRecords.Except(oldRecords, s_recordPKComparer).ToList();              //一定要指定Comparer

            var needCompareRecords = oldRecords.Except(dropRecords, s_recordPKComparer)?.ToList();
            needCompareRecordIdentifiers = needCompareRecords?.Select(t => t.GetValue(pkColName)).ToList();

            if (needCompareRecordIdentifiers != null)
            {
                foreach (var pk in needCompareRecordIdentifiers)
                {
                    var oldRecord = oldRecords.FirstOrDefault(r => r.GetPKValue(cno) == pk.ToString());
                    var newRecord = newRecords.FirstOrDefault(r => r.GetPKValue(cno) == pk.ToString());
                    //用前端传入的只包含id的record的changeSet刷新OldValue
                    RefreshTRecord(cdo, cno, oldRecord, newRecord);
                    //需要递归比较变更
                    if (cno.ChildCompositeNodeObjects != null)
                    {
                        foreach (var childCNO in cno.ChildCompositeNodeObjects)
                        {
                            var isList = childCNO.GetRelationType(cno) == 0;
                            var nodeAlias = TUtil.GetPropertyName(childCNO.DataObjectTableAlias, isList);

                            List<TRecord> oldChildRecords = GetChildNodesRecords(oldRecord, nodeAlias, isList);
                            List<TRecord> newChildRecords = GetChildNodesRecords(newRecord, nodeAlias, isList);

                            if ((newChildRecords != null && newChildRecords.Count > 0) || (oldChildRecords != null && oldChildRecords.Count > 0))
                            {
                                GetUpdateChangeSet(cdo, childCNO, oldChildRecords, newChildRecords, newRecord, depth + 1);
                            }
                        }
                    }
                }
            }
            if (addRecords != null && addRecords.Count > 0)
            {
                //新增数据只有NewRecord中有，不需要做合并
                GetCreateChangeSetRecursive(cno, addRecords, depth);
            }
            if (dropRecords != null && dropRecords.Count > 0)
            {
                //删除的数据需要归并到new Record中
                dropRecords.ForEach(r => r.ChangeType = TRecordChangeType.Delete);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cno"></param>
        /// <param name="oldRecord">DB中取出来的Record</param>
        /// <param name="newRecord">前端传过来的只有ChangeSet的TRecord</param>
        /// <returns></returns>
        private static void RefreshTRecord(CompositeDataObject cdo, CompositeNodeObject cno, TRecord oldRecord, TRecord newRecord)
        {
            if (cno == null || oldRecord == null && newRecord == null) return;
            if (newRecord.ChangeSet == null && newRecord.ChangeSet.ChangeType == TRecordChangeType.UnChanged) return;
            var pkName = cdo?.RootCompositeNodeObject?.GetPKColumn()?.ColumnName;
            var oldPkVal = oldRecord.GetPKValue(cno);
            var newPkVal = newRecord.GetPKValue(cno);
            
            if (!string.Equals(oldPkVal, newPkVal))
            {
                throw new FasException(DataAccessErrorCode.PlugIn.Error, string.Format(CommonStringClassEngine.TRecordService_RefreshTRecord));
            }

            if (newRecord.ChangeSet.ChangeType == TRecordChangeType.Update)
            {
                //根据当前节点record的变更集changeSet内容刷新db中取出的record信息
                foreach (var item in newRecord.ChangeSet.ChangeSetItems)
                {
                    ////跳过状态字段的刷新赋值 ** 现不能跳过
                    //if (cno.IsRootObject && statFields != null && statFields.Count > 0)
                    //{
                    //    continue;
                    //}
                    oldRecord.SetValue(item.ColumnCode, item.NewValue);
                }
            }
            else if (newRecord.ChangeSet.ChangeType == TRecordChangeType.Delete)
            {
                oldRecord.ChangeSet = new TChangeSet
                {
                    BillID = oldPkVal,
                    CDOID = cdo.ID,
                    CDOCode = cdo.Code,
                    ChangeType = TRecordChangeType.Delete,
                    PKColumnName = pkName,
                    PKColumnValue = oldPkVal,
                    ModifyTime = DateTime.Now,
                    Modifier = UserUtil.GetCurrentUserID(),
                    ModifierName = UserUtil.GetCurrentUserName(),
                    TenantID = UserUtil.GetCurrentUserTenantId(),
                };
            }
            else if (newRecord.ChangeSet.ChangeType == TRecordChangeType.Add)
            {
                InitAddTRecordChangeSet(cno, oldRecord);
            }
        }

        /// <summary>
        /// Create变更初始化，获取当前节点的TRecord的变更，要求TRecord的主键列必须赋值
        /// </summary>
        /// <param name="cno"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        private static void InitAddTRecordChangeSet(CompositeNodeObject cno, TRecord record)
        {
            var normalEle = cno.Elements.Where(e => e.ElementType == ElementType.Normal).ToList();
            string innerAssoEleName = null;
            var pkColName = cno.GetPKColumn().ColumnName;
            if (!cno.IsRootObject)
            {
                var innerAssoEle = cno.GetInnerAssociateElement();
                innerAssoEleName = innerAssoEle.Alias ?? innerAssoEle.Name;
            }
            var billID = record.GetValue(pkColName)?.ToString();
            record.ChangeType = TRecordChangeType.Add;
            record.ChangeSet = new TChangeSet
            {
                ChangeType = TRecordChangeType.Add,
                BillID = billID,
                CDOID = cno.CompositeDataObjectID,
                CDOCode = cno.CompositeDataObject?.Code,
                PKColumnName = pkColName,
                PKColumnValue = billID,
                ModifyTime = DateTime.Now,
                Modifier = UserUtil.GetCurrentUserID(),
                ModifierName = UserUtil.GetCurrentUserName(),
                TenantID = UserUtil.GetCurrentUserTenantId(),
            };

            var dataChangeSetItems = new List<TChangeSetItem>();
            foreach (var ele in normalEle)
            {
                var col = ele.GetDataColumn(cno);
                if (record.IsContainProperty(col.ColumnName))
                {
                    //新增时DBNull类型的不用记录
                    object newVal = record.GetValue(col.ColumnName);
                    if (newVal is DBNull || newVal == null || string.IsNullOrEmpty(newVal?.ToString())) continue;

                    dataChangeSetItems.Add(new TChangeSetItem
                    {
                        ColumnCode = ele.Alias ?? ele.Name,
                        ColumnName = ele.DisplayName,
                        InitValue = null,
                        InitDisplayName = null,
                        OldValue = null,
                        OldDisplayValue = null,
                        NewValue = newVal,
                        NewDisplayValue = null,
                        SortOrder = ele.Sort,
                        DataType = ele.DataType,
                    });
                }
            }
            record.ChangeSet.AddRangeChangeData(dataChangeSetItems);
        }

        private static List<TRecord> GetChildNodesRecords(TRecord currRecord, string childAlias, bool isList)
        {
            object childObj = currRecord[childAlias];
            var result = new List<TRecord>();
            if (childObj == null)
                return result;
            if (isList)
            {
                if (childObj is List<TRecord> listRecord)
                    return listRecord;
            }
            else
            {
                if (childObj is TRecord record)
                {
                    if (record.Count == 0)
                        return result;
                    return new List<TRecord> { record };
                }
            }
            throw new FasException(DataAccessErrorCode.CompositeDataObject.Error, string.Format(CommonStringClassEngine.TRecordService_GetChildNodesRecords2,childAlias,childObj.GetType(),(isList ? CommonStringClassEngine.TRecordService_GetChildNodesRecords: CommonStringClassEngine.TRecordService_GetChildNodesRecords1)));
        }

        private static TRecord GetTRecordFromDB(CompositeDataObject cdo, string pkVal)
        {
            try
            {
                var result = s_saveService.QueryData(cdo, pkVal, DACControlContextHelper.BuildDefultExtParams()) as TCommonResult<TRecord>;
                return result.Record;
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.PlugIn.Error, string.Format(CommonStringClassEngine.TRecordService_GetTRecordFromDB,e.Message), e);
            }
        }

        private static object PreprocessField(object arg)
        {
            if (arg is JValue val)
            {
                return val.Value;
            }
            else if (arg is DBNull)
            {
                return null;
            }
            return arg;
        }

        private static void InitRecord(List<TRecord> records, CompositeNodeObject cno)
        {
            if (records != null)
            {
                foreach (var record in records)
                {
                    if (record != null)
                    {
                        record.PKColumnName = cno?.GetPKColumn()?.ColumnName;
                        record.PKColumnValue = record.GetValue(record.PKColumnValue)?.ToString();
                        record.TimestampColumnName = cno?.GetTimestampColumnForEngine()?.ColumnName;
                    }
                }
            }
        }

        private static void GetCreateChangeSetRecursive(CompositeNodeObject cno, List<TRecord> newRecords, int depth = 0)
        {
            foreach (var record in newRecords)
            {
                InitAddTRecordChangeSet(cno, record);
            }
            if (cno.ChildCompositeNodeObjects != null)
            {
                foreach (var childCNO in cno.ChildCompositeNodeObjects)
                {
                    List<TRecord> childRecords = new List<TRecord>();
                    //根据子节点CNO与当前节点的关联类型，在TRecord中取出子节点集合
                    var relationtype = childCNO.GetRelationType();
                    foreach (var record in newRecords)
                    {
                        var childData = record[childCNO.GetCNODataObjectTableAlias()];
                        if (childData != null)
                        {
                            if (relationtype == 0)
                            {
                                if (childData is List<TRecord> cRecords)
                                {
                                    childRecords.AddRange(cRecords);
                                }
                            }
                            else
                            {
                                if (childData is TRecord cRecord)
                                {
                                    childRecords.Add(cRecord);
                                }
                            }
                        }
                    }
                    if (childRecords.Count > 0)
                    {
                        GetCreateChangeSetRecursive(childCNO, childRecords, depth + 1);
                    }
                }
            }
        }

        private static List<string> GetStateFields(CompositeNodeObject cno)
        {
            if (!cno.IsRootObject) return null;
            return cno.Elements.Where(e => e.StateMachines != null && e.StateMachines.Count > 0)
                      .Select(e => e.Alias ?? e.Name)
                      .ToList();
        }
    }
}
